// ResString.cpp: implementation of the CResString class.
//
//////////////////////////////////////////////////////////////////////

#include "../headers/ResString.h"
#include "../headers/win_adapter.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CResString::CResString()
{
	pStrBlock = NULL;
	hInstLib  = NULL;
	iTypeIndex = -1;
	iNameIndex = -1;
	bContainsType = false;
	scAppPath = "";
}

CResString::CResString(string scPath)
{
	pStrBlock = NULL;
	hInstLib  = NULL;
	iTypeIndex = -1;
	iNameIndex = -1;
	bContainsType = false;
	scAppPath = scPath;
}

CResString::~CResString()
{
	// Free up all memory used by our own stringblock
	this->DeleteStringBlock();

	if(hInstLib != NULL)
	{
		// Free the library.
		FreeLibrary(hInstLib);
		vScTypes.clear();
		vScNames.clear();
	}
}

void CResString::LoadFile(string scPath)
{
	scAppPath = scPath;
	if(hInstLib != NULL)
	{
		// Free the library.
		FreeLibrary(hInstLib);
		vScTypes.clear();
		vScNames.clear();
		iTypeIndex = -1;
		iNameIndex = -1;
		hInstLib = NULL;
	}
}

int CResString::GetStringBlock(UINT nBlockID, WORD wLangID)
{
	HRSRC	     hFindRes  = NULL;
	HGLOBAL	     hLoadRes  = NULL;
	LPVOID	     pRes      = NULL;

	if(NULL == hInstLib)
	{
		if(!scAppPath.length())
		{
			return FILE_NOT_LOADED;
		}
		hInstLib = LoadLibraryExA( scAppPath.c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE );
		if( NULL == hInstLib )
		{
			return LIBRARY_LOAD_FAILED;
		}
	}

	hFindRes = FindResourceEx(hInstLib, RT_STRING, MAKEINTRESOURCE(nBlockID), wLangID);
	if( NULL == hFindRes )
	{
		return RESOURCE_NOT_FOUND;
	}

	hLoadRes = LoadResource(hInstLib, hFindRes);
	if( NULL == hLoadRes )
	{
		return NO_LOAD_RESTABLE;
	}

	pRes = LockResource(hLoadRes);
	if( NULL == pRes )
	{
		return NO_LOAD_RESTABLE;
	}

	// Allocate sufficient memory to our own stringblock
	pStrBlock = (PSTRINGBLOCK)malloc(sizeof(STRINGBLOCK));
	if( NULL == pStrBlock )
	{
		return INSUFFICIENT_MEMORY;
	}

	// Set string block details
	pStrBlock->nBlockID = nBlockID;
	pStrBlock->wLangID  = wLangID;
	if( SUCCESS_MADE != BuildStringBlock(pRes) )
	{
		return NO_BUILD_RESTABLE;
	}

	return SUCCESS_MADE;
}

int CResString::GetStringBlock(UINT nBlockID, LanguageID langID )
{
	HRSRC	     hFindRes  = NULL;
	HGLOBAL	     hLoadRes  = NULL;
	LPVOID	     pRes      = NULL;

	if(NULL == hInstLib)
	{
		if(!scAppPath.length())
		{
			return FILE_NOT_LOADED;
		}
		hInstLib = LoadLibraryExA( scAppPath.c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE );
		if( NULL == hInstLib )
		{
			return LIBRARY_LOAD_FAILED;
		}
	}

	hFindRes = FindResourceEx(hInstLib, RT_STRING, MAKEINTRESOURCE(nBlockID), langID);
	if( NULL == hFindRes )
	{
		return RESOURCE_NOT_FOUND;
	}

	hLoadRes = LoadResource(hInstLib, hFindRes);
	if( NULL == hLoadRes )
	{
		return NO_LOAD_RESTABLE;
	}

	pRes = LockResource(hLoadRes);
	if( NULL == pRes )
	{
		return NO_LOAD_RESTABLE;
	}

	// Allocate sufficient memory to our own stringblock
	pStrBlock = (PSTRINGBLOCK)malloc(sizeof(STRINGBLOCK));
	if( NULL == pStrBlock )
	{
		return INSUFFICIENT_MEMORY;
	}

	// Set string block details
	pStrBlock->nBlockID = nBlockID;
	pStrBlock->wLangID  = langID;
	if( SUCCESS_MADE != BuildStringBlock(pRes) )
	{
		return NO_BUILD_RESTABLE;
	}

	return SUCCESS_MADE;
}

int CResString::BuildStringBlock(LPVOID pRes)
{
	int		nLen;
	WCHAR*	pParse = (WCHAR *)pRes;
	
	// There are NO_OF_STRINGS_PER_BLOCK(=16) strings per block.
	for( int i = 0; i < NO_OF_STRINGS_PER_BLOCK; i++ )
	{
		nLen = (int)*pParse++;														// The length of the string.
		pStrBlock->strArray[i] = (LPWSTR)malloc((nLen + 1) * sizeof(WCHAR));

		if( NULL == pStrBlock->strArray[i] )
		{
			for( int k = 0; k < i; k++ )											// Free up the memory allocated so far.
				free(pStrBlock->strArray[k]);
			return INSUFFICIENT_MEMORY;
		}

		for( int j = 0; j < nLen; j++ )												// Copy the string.
			pStrBlock->strArray[i][j] = *pParse++;
		pStrBlock->strArray[i][j] = 0;
	}
	return SUCCESS_MADE;
}

void CResString::BuildResBlock( LPVOID pRes )
{
	int		nLen;
	WCHAR*	pParse = (WCHAR *)pRes;

	// There are NO_OF_STRINGS_PER_BLOCK (= 16) strings per block.
	for( int i = 0; i < NO_OF_STRINGS_PER_BLOCK; i++ )
	{
		*pParse++ = nLen = (int)wcslen(pStrBlock->strArray[i]);
		for( int j = 0; j < nLen; j++ )
			*pParse++ = pStrBlock->strArray[i][j];
	}
}

int CResString::GetString( UINT nIndex, LPSTR pszText )
{
	if( NULL == pStrBlock )
	{
		return INVALID_BLOCK;
	}
	if( (nIndex < 0) || (nIndex >= NO_OF_STRINGS_PER_BLOCK) )
	{
		return INVALID_BLOCK_INDEX;
	}
	if( NULL == pszText )
	{
		return INVALID_STRING;
	}

	if( !WideCharToMultiByte(CP_ACP, 0, pStrBlock->strArray[nIndex], -1, pszText, (int)wcslen(pStrBlock->strArray[nIndex]) + 1, NULL, NULL) )
	{
		return UNKNOWN_ERROR;
	}

	return SUCCESS_MADE;
}

int CResString::GetWString( UINT nIndex, LPWSTR pswzText )
{
	if( NULL == pStrBlock )
	{
		return INVALID_BLOCK;
	}
	if( (nIndex < 0) || (nIndex >= NO_OF_STRINGS_PER_BLOCK) )
	{
		return INVALID_BLOCK_INDEX;
	}
	if( NULL == pswzText )
	{
		return INVALID_STRING;
	}
	wcscpy(pswzText, pStrBlock->strArray[nIndex]);

	return SUCCESS_MADE;
}


int CResString::GetStringLength( UINT nIndex )
{
	if( NULL == pStrBlock )
	{
		//AfxMessageBox("Invalid Block...");
		return -1;
	}
	if( (nIndex < 0) || (nIndex >= NO_OF_STRINGS_PER_BLOCK) )
	{
		//AfxMessageBox("Invalid string block id...");
		return -1;
	}

	return (int)wcslen(pStrBlock->strArray[nIndex]);
}

DWORD CResString::GetResSize()
{
	DWORD dwResSize = 0;
	int i = 0;

	for( i = 0; i < NO_OF_STRINGS_PER_BLOCK; i++ )
		dwResSize += ((int)wcslen(pStrBlock->strArray[i]) + 1);

	return dwResSize * sizeof(WCHAR);
}

int CResString::DeleteStringBlock()
{
	if( NULL == pStrBlock )
	{
		return INVALID_BLOCK;
	}

	for( int i = 0; i < NO_OF_STRINGS_PER_BLOCK; i++ )
	{
		if( pStrBlock->strArray[i] )
			free(pStrBlock->strArray[i]);
	}
	free( pStrBlock );

	return true;
}

int CResString::SetString( UINT nIndex, string scText )
{
	if( NULL == pStrBlock )
	{
		return INVALID_BLOCK;
	}
	if( (nIndex < 0) || (nIndex >= NO_OF_STRINGS_PER_BLOCK) )
	{
		return INVALID_BLOCK_INDEX;
	}
	
	// Delete the current string & reallocate a new one..
	free(pStrBlock->strArray[nIndex]);

	int nLen = (int)scText.length() + 1;
	pStrBlock->strArray[nIndex] = (LPWSTR)malloc( sizeof(WCHAR) * nLen);

	if( NULL == pStrBlock->strArray[nIndex] )
	{
		return INSUFFICIENT_MEMORY;
	}

	if( !MultiByteToWideChar(CP_ACP, 0, scText.c_str(), -1, pStrBlock->strArray[nIndex], nLen) )
	{
		return UNKNOWN_ERROR;
	}

	return SUCCESS_MADE;
}


int CResString::UpdateResourceBlock(UINT nBlockID, WORD wLangID)
{
	BOOL	bUpdated;
	HANDLE	hUpdate;
	DWORD	dwResSize = 0;
	LPVOID	pRes;
	WORD	wLanguageID = (0 == wLangID) ? pStrBlock->wLangID : wLangID;

	if( NULL == pStrBlock )
	{
		return INVALID_BLOCK;
	}

	// Get the resource length as required by a raw string resource block.
	dwResSize = GetResSize();
	pRes = malloc(dwResSize);
	if( NULL == pRes )
	{
		return INSUFFICIENT_MEMORY;
	}

	BuildResBlock( pRes );

	if(hInstLib != NULL)
	{
		// Free the library.
		FreeLibrary(hInstLib);
		hInstLib = NULL;
	}
	if(!scAppPath.length())
	{
		return FILE_NOT_LOADED;
	}

	hUpdate = BeginUpdateResource(scAppPath.c_str(), FALSE);
	if( NULL == hUpdate )
	{
		free( pRes );
		return FAILED_WRITING_BLOCK;
	}

	// Update the resource.
	bUpdated = UpdateResource( hUpdate, RT_STRING, MAKEINTRESOURCE(((-1 == nBlockID) ? pStrBlock->nBlockID : nBlockID)), wLanguageID, pRes, dwResSize );
	if( bUpdated == FALSE )
	{
		free( pRes );
		return FAILED_WRITING_BLOCK;
	}

	if( !EndUpdateResource(hUpdate, FALSE) )
	{
		free( pRes );
		return FAILED_WRITING_BLOCK;
	}

	free( pRes );

	return SUCCESS_MADE;
}

int CResString::UpdateResourceBlock(UINT nBlockID, LanguageID langID)
{
	BOOL	bUpdated;
	HANDLE	hUpdate;
	DWORD	dwResSize = 0;
	LPVOID	pRes;
	WORD	wLanguageID = (0 == langID) ? pStrBlock->wLangID : langID;

	if( NULL == pStrBlock )
	{
		return INVALID_BLOCK;
	}

	// Get the resource length as required by a raw string resource block.
	dwResSize = GetResSize();
	pRes = malloc(dwResSize);
	if( NULL == pRes )
	{
		return INSUFFICIENT_MEMORY;
	}

	BuildResBlock( pRes );

	if(hInstLib != NULL)
	{
		// Free the library.
		FreeLibrary(hInstLib);
		hInstLib = NULL;
	}
	if(!scAppPath.length())
	{
		return FILE_NOT_LOADED;
	}

	hUpdate = BeginUpdateResource(scAppPath.c_str(), FALSE);
	if( NULL == hUpdate )
	{
		free( pRes );
		return FAILED_WRITING_BLOCK;
	}

	// Update the resource.
	bUpdated = UpdateResource( hUpdate, RT_STRING, MAKEINTRESOURCE(((-1 == nBlockID) ? pStrBlock->nBlockID : nBlockID)), wLanguageID, pRes, dwResSize );
	if( bUpdated == FALSE )
	{
		free( pRes );
		return FAILED_WRITING_BLOCK;
	}

	if( !EndUpdateResource(hUpdate, FALSE) )
	{
		free( pRes );
		return FAILED_WRITING_BLOCK;
	}

	free( pRes );

	return SUCCESS_MADE;
}

string CResString::GetString( UINT nIndex )
{
	string scText;
	LPSTR pText;
	int nLen = GetStringLength( nIndex );				// Determin length of string
	
	pText = (LPSTR)malloc(sizeof(LPSTR) * nLen);		// Allocate memory for string
	if( this->GetString( nIndex, pText ) )					// Retrieve string and convert from widechar
	{	
		scText = pText;	
		free( pText );									// Free memory from retrieving text
		return scText;
	}else
		return "";										// Return string;
}

wstring CResString::GetWString( UINT nIndex )
{
	wstring wscText;
	LPWSTR pwText;
	int nLen = GetStringLength( nIndex );				// Determin length of string
	
	pwText = (LPWSTR)malloc(sizeof(LPWSTR) * nLen);		// Allocate memory for string
	if( this->GetWString( nIndex, pwText ) )			// Retrieve string
	{	
		wscText = pwText;	
		free( pwText );									// Free memory from retrieving text
		return wscText;
	}else
		return L"";										// Return string;
}

LPCSTR CResString::GetFirstType()
{
	if(!vScTypes.size())
	{
		if(NULL == hInstLib)
		{
			if(!scAppPath.length())
			{
				return "";
			}
			hInstLib = LoadLibraryExA( scAppPath.c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE );
			if( NULL == hInstLib )
			{
				return "";
			}
		}
		win::EnumResourceTypes(hInstLib, this, &CResString::EnumResTypes, (LONG)0);
		if(!vScTypes.size())
		{
			return "";
		}
	}
	if(vScTypes.size())
	{
		iTypeIndex = 0;
		return vScTypes.front().c_str();
	}
	return "";
}

LPCSTR CResString::GetNextType()
{
	if(iTypeIndex == -1)
	{
		return "";
	}
	iTypeIndex++;
	if((unsigned int)iTypeIndex < vScTypes.size())
	{
		return vScTypes[iTypeIndex].c_str();
	}
	else
	{
		return "";
	}
}

BOOL CResString::EnumResTypes( HMODULE hModule, LPTSTR lpType, LONG lParam)
{
	if(IS_INTRESOURCE(lpType))
	{
		char strInt[5];
		sprintf(strInt, "#%u", (USHORT)lpType);
		iTypeIndex = 0;
		vScTypes.push_back(strInt);
	}
	else
	{
		vScTypes.push_back(lpType);
	}
	return true;
}

bool CResString::ContainsType(LPTSTR strType)
{
	if(NULL == hInstLib)
	{
		if(!scAppPath.length())
		{
			return false;
		}
		hInstLib = LoadLibraryExA( scAppPath.c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE );
		if( NULL == hInstLib )
		{
			return false;
		}
	}

	bContainsType = false;
	win::EnumResourceTypes(hInstLib, this, &CResString::FindResTypeStr, (LONG)&strType);
	return bContainsType;
}

BOOL CResString::FindResTypeStr( HMODULE hModule, LPTSTR lpType, LONG lParam)
{
	LPTSTR strType = (LPTSTR)lParam;
	if(!IS_INTRESOURCE(lpType))
	{
		if(strType == lpType)
		{
			bContainsType = true;
			return false;
		}
	}
	return true;
}

bool CResString::ContainsType(USHORT iType)
{
	if(NULL == hInstLib)
	{
		if(!scAppPath.length())
		{
			return false;
		}
		hInstLib = LoadLibraryExA( scAppPath.c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE );
		if( NULL == hInstLib )
		{
			return false;
		}
	}

	bContainsType = false;
	win::EnumResourceTypes(hInstLib, this, &CResString::FindResTypeStr, (LONG)iType);
	return bContainsType;
}

BOOL CResString::FindResTypeInt( HMODULE hModule, LPTSTR lpType, LONG lParam)
{
	if(IS_INTRESOURCE(lpType))
	{
		if(MAKEINTRESOURCE((USHORT)lParam) == lpType)
		{
			bContainsType = true;
			return false;
		}
	}
	return true;
}

LPCSTR CResString::GetFirstNameInt(USHORT iType)
{
	return this->GetFirstNameStr(MAKEINTRESOURCE(iType));
}

LPCSTR CResString::GetFirstNameStr(LPCTSTR strType)
{
	if(!vScNames.size())
	{
		if(NULL == hInstLib)
		{
			if(!scAppPath.length())
			{
				return "";
			}
			hInstLib = LoadLibraryExA( scAppPath.c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE );
			if( NULL == hInstLib )
			{
				return "";
			}
		}
		win::EnumResourceNames(hInstLib, strType, this, &CResString::EnumResNames, (LONG)0);
		if(!vScNames.size())
		{
			return "";
		}
	}

	iNameIndex = 0;
	return vScNames.front().c_str();
}

LPCSTR CResString::GetNextName()
{
	if(iNameIndex == -1)
	{
		return "";
	}
	iNameIndex++;
	if((unsigned int)iNameIndex < vScNames.size())
	{
		return vScNames[iNameIndex].c_str();
	}

	return "";
}

BOOL CResString::EnumResNames( HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG lParam)
{
	if(IS_INTRESOURCE(lpszName))
	{
		char strInt[5];
		sprintf(strInt, "#%u", (USHORT)lpszName);
		vScNames.push_back(strInt);
	}
	else
	{
		vScNames.push_back(lpszName);
	}
	return true;
}